﻿namespace TNet
{
    using System;
    using System.IO;
    using System.Net;
    using System.Threading;

    public class UdpLobbyServer : LobbyServer
    {
        private TNet.Buffer mBuffer;
        private ServerList mList = new ServerList();
        private Thread mThread;
        private long mTime;
        private UdpProtocol mUdp;

        public override void AddServer(string name, int playerCount, IPEndPoint internalAddress, IPEndPoint externalAddress)
        {
            this.mList.Add(name, playerCount, internalAddress, externalAddress, this.mTime);
        }

        private BinaryWriter BeginSend(Packet packet)
        {
            this.mBuffer = TNet.Buffer.Create();
            return this.mBuffer.BeginPacket(packet);
        }

        private void EndSend(IPEndPoint ip)
        {
            this.mBuffer.EndPacket();
            this.mUdp.Send(this.mBuffer, ip);
            this.mBuffer.Recycle();
            this.mBuffer = null;
        }

        private bool ProcessPacket(TNet.Buffer buffer, IPEndPoint ip)
        {
            BinaryReader reader = buffer.BeginReading();
            switch (((Packet) reader.ReadByte()))
            {
                case Packet.RequestAddServer:
                    if (reader.ReadUInt16() == 1)
                    {
                        ServerList.Entry newEntry = new ServerList.Entry();
                        newEntry.ReadFrom(reader);
                        if (newEntry.externalAddress.Address.Equals(IPAddress.None))
                        {
                            newEntry.externalAddress = ip;
                        }
                        this.mList.Add(newEntry, this.mTime);
                        return true;
                    }
                    return false;

                case Packet.RequestRemoveServer:
                    if (reader.ReadUInt16() == 1)
                    {
                        IPEndPoint point;
                        IPEndPoint point2;
                        Tools.Serialize(reader, out point);
                        Tools.Serialize(reader, out point2);
                        if (point2.Address.Equals(IPAddress.None))
                        {
                            point2 = ip;
                        }
                        this.RemoveServer(point, point2);
                        return true;
                    }
                    return false;

                case Packet.RequestServerList:
                    if (reader.ReadUInt16() == 1)
                    {
                        this.mList.WriteTo(this.BeginSend(Packet.ResponseServerList));
                        this.EndSend(ip);
                        return true;
                    }
                    return false;

                case Packet.RequestPing:
                    this.BeginSend(Packet.ResponsePing);
                    this.EndSend(ip);
                    break;
            }
            return false;
        }

        public override void RemoveServer(IPEndPoint internalAddress, IPEndPoint externalAddress)
        {
            this.mList.Remove(internalAddress, externalAddress);
        }

        public override bool Start(int listenPort)
        {
            this.Stop();
            this.mUdp = new UdpProtocol();
            if (!this.mUdp.Start(listenPort))
            {
                return false;
            }
            this.mThread = new Thread(new ThreadStart(this.ThreadFunction));
            this.mThread.Start();
            return true;
        }

        public override void Stop()
        {
            if (this.mThread != null)
            {
                this.mThread.Abort();
                this.mThread = null;
            }
            if (this.mUdp != null)
            {
                this.mUdp.Stop();
                this.mUdp = null;
            }
            this.mList.Clear();
        }

        private void ThreadFunction()
        {
            while (true)
            {
                TNet.Buffer buffer;
                IPEndPoint point;
                this.mTime = DateTime.UtcNow.Ticks / 0x2710L;
                this.mList.Cleanup(this.mTime);
                while (((this.mUdp != null) && (this.mUdp.listeningPort != 0)) && this.mUdp.ReceivePacket(out buffer, out point))
                {
                    try
                    {
                        this.ProcessPacket(buffer, point);
                    }
                    catch (Exception)
                    {
                    }
                    if (buffer != null)
                    {
                        buffer.Recycle();
                        buffer = null;
                    }
                }
                Thread.Sleep(1);
            }
        }

        public override bool isActive
        {
            get
            {
                return ((this.mUdp != null) && this.mUdp.isActive);
            }
        }

        public override int port
        {
            get
            {
                return (!this.mUdp.isActive ? 0 : this.mUdp.listeningPort);
            }
        }
    }
}

